#Import Modules
import os, pygame, math
from pygame.locals import *

# Load up the parent class
from PhysicsObject import *

class Character(PhysicsObject):
    """this object contains the player character"""
    def __init__(self, spawnpoint, controller):
        PhysicsObject.__init__(self,spawnpoint) #call PhysicsObject initializer
        
        # Event flags (r/o)
        # You have no control over when there happen, but you can react to them.
        self.landing = 0
        self.ceilingbump = 0
        self.walk = 0
        
        # Controller object (r/o)
        self.controller = controller
        
        # Event flags (r/w)
        # You have full control over when these happen. The game reacts to them.
        self.jump = 0 # Is the character jumping?
                
        # Movement Constants.
        self.walkaccel = 0.5    # Acceleration of walking
        self.walklimit = 4.0    # Maximum speed at which walking can still add up
        self.runaccel = 0.2     # How much faster running is on top of walking
        self.runlimit = 4.0     # How much faster the running limit is compared to walking
        
        # Movement Variables
        self.thrustx = 0.0      # Current (attempted) walking accel
        self.moveaccel = 0      # Current movement acceleration
        self.movelimit = 0      # Current maximum speed
        
        self.jumpthrust = -1.5     # Must be more than gravity or else jumping fails
        self.falllimit = 6.0    # The speed at which fast fall cuts out (note that the global speed limit trumps all)
        self.maxjumpframes = 15 # The number of frames before jump thrust cuts out
        self.jumpframes = 0
     
    # No need to override       
    #def die(self):
        # Run the parent class's die function
    #    PhysicsObject.die(self)
    
    def hitceiling(self): pass
            
    def physics(self):
    
        # Apply the parent object's physics. 
        # Takes care of basic position, velocity and acceleration/friction.
        PhysicsObject.physics(self)
        
        # Reset a few oneshot flags
        self.landing = 0
        self.ceilingbump = 0
        
        if collision(self,PhysicsObject.wall):
            
            # Zip the sprite out of the mask by tracking backwards, towards where it was last frame. 
            # This should always work, but it might be a little on the computationally expensive side.
            newposx = self.positionx
            newposy = self.positiony
            
            # Initialize character event flags
            oldposx = self.lastpositionx
            oldposy = self.lastpositiony
            
            if abs(newposx-oldposx) > abs(newposy-oldposy):
                crawlx = cmp((newposx-oldposx),0)
                crawly = ((newposy-oldposy)/abs(newposx-oldposx))
            elif abs(newposx-oldposx) < abs(newposy-oldposy):
                crawly = cmp((newposy-oldposy),0)
                crawlx = ((newposx-oldposx)/abs(newposy-oldposy))
            elif (newposx != oldposx):
                crawlx = cmp((newposx-oldposx),0)
                crawly = cmp((newposy-oldposy),0)
            else:
                print "Collision Error: Hit wall without moving!"
            
            self.positionx = oldposx
            self.positiony = oldposy
            
            if math.floor(self.positionx) != math.floor(newposx):
                xgo = True
            else:
                xgo = False
            if math.floor(self.positiony) != math.floor(newposy):
                ygo = True
            else:
                ygo = False
            
            while xgo or ygo:
                if xgo:
                    self.positionx = self.positionx + crawlx
                    if collision(self,PhysicsObject.wall):
                        # We hit something, back X up and stop crawl
                        self.positionx = self.positionx - crawlx
                        self.speedx = 0
                        xgo = False
                    elif math.floor(self.positionx) == math.floor(newposx):
                        # We've arrived. Transfer value to save subpixel positon
                        self.positionx = newposx
                        # Turn off xgo
                        xgo = False
                        
                if ygo:
                    self.positiony = self.positiony + crawly
                    if collision(self,PhysicsObject.wall):
                        self.positiony = self.positiony - crawly
                        #if self.speedy > 0:
                        self.speedy = 0
                        ygo = False
                    elif math.floor(self.positiony) == math.floor(newposy):
                        # We've arrived. Transfer value to save subpixel positon
                        self.positiony = newposy
                        # Turn off ygo
                        ygo = False
                        
        self.lastpositionx = self.positionx
        self.lastpositiony = self.positiony
        #print "(" + str(self.positionx) + ", " + str(self.positiony) + ")"
        
        # seekground() can lodge the self into a wall, so we need to store our last safe position for next time
        gfound, yvel = seekground(self, PhysicsObject.ground)
        if gfound:
            if self.walk == 0:
                self.landing = 1
                if self.speedy > 0:
                    self.speedy = 0
                self.walk = 1
                self.accelerationy = 0

            self.groundy = yvel
        else:
            self.walk = 0
            self.accelerationy = self.gravity
            
    def hit(self, damage, stun=10, direction=-1, knockback=5):
        if PhysicsObject.hit(self, damage, stun, direction, knockback):
            self.thrustx = 0
            self.thrusty = 0
            self.jumpframes = self.maxjumpframes
            if direction != -1:
                if abs(direction) < math.pi/2:
                    self.speedx += knockback
                else:
                    self.speedx -= knockback
            return True
        return False
        
    
    def update(self):
        # This method runs once per frame. Use it for control/AI, animation, and sound effects 
        # Calculate controlled horizontal movement (walking and air control) 
        # Add thrust accel to speed if the self has not broken the walking speed limit.
        
        PhysicsObject.update(self)
        
        self.thrustx = 0
        
        if self.controller != None and self.stun == 0:
            self.movelimit = self.walklimit + self.controller.run * self.runlimit
            self.moveaccel = self.walkaccel + self.controller.run * self.runaccel
            if self.controller.left == 1:
                self.thrustx = -self.moveaccel
            if self.controller.right == 1:
                self.thrustx = self.moveaccel
        
        if self.walk == 1:
            if math.pow(math.pow(self.speedx,2)+math.pow(self.speedy,2),0.5) < self.movelimit:
                self.speedx = self.speedx + self.thrustx
        else:
        # This section refers to air control. Currently air control is slower to respond but capable of the same max manuverability.
            if abs(self.speedx) < self.movelimit:
                self.speedx = self.speedx + (self.thrustx / 2)
        
        # Calculate controlled vertical movement (jumping and fast falling)
        if self.controller.jump == 1 and self.walk == 1 and self.jumpframes == 0 and self.stun == 0:
            self.jump = 1
            # For a better feel, give the jump a good blast to start. 5x the normal thrust on the first frame.
            self.speedy = self.speedy + (self.jumpthrust * 4)
        
        if self.controller.jump == 0:
            self.jump = 0
            if self.walk == 1:
                self.jumpframes = 0
                
        if self.ceilingbump == 1:
            self.jumpframes = self.maxjumpframes
            self.ceilingbump = 0
        
        if self.jump == 1 and self.jumpframes < self.maxjumpframes:
            self.speedy = self.speedy + self.jumpthrust
            self.jumpframes = self.jumpframes + 1